%{ open Ast %}

%token LPAREN RPAREN LBRACE RBRACE LBRACKET RBRACKET
%token SEMI COMMA DOT QUOTE
%token PLUS MINUS TIMES DIVIDE MOD PLUSPLUS MINUSMINUS
%token PLUSEQ MINUSEQ TIMESEQ DIVIDEEQ MODEQ
%token EQ NEQ LT LEQ GT GEQ AND NOT OR ASSIGN
%token IF ELSE ELSEIF FOR WHILE RETURN 

%token INT VOID BOOL STRING CARD LIST LABEL
%token <int> INTLITERAL
%token <bool> BOOLEANLITERAL
%token <string> STRINGLITERAL
%token <string> CARDLITERAL
%token <string> LABELLITERAL
%token <string> ID
%token <string> TYPE
%token EOF

%nonassoc NOELSE
%nonassoc ELSE
%nonassoc LPAREN
%right ASSIGN
%left PLUSEQ MINUSEQ TIMESEQ DIVIDEEQ MODEQ	
%left AND OR
%left EQ NEQ LT GT LEQ GEQ
%left PLUS MINUS 
%left TIMES DIVIDE MOD
%left PLUSPLUS MINUSMINUS
%right NOT
%start program
%type <Ast.program> program

%%

program:
	/* nothing */ { [], [] }
	| program vdecl { ($2 :: fst $1), snd $1 }
	| program fdecl { fst $1, ($2 :: snd $1) }

fdecl:
	TYPE ID LPAREN formals_opt RPAREN LBRACE vdecl_list stmt_list RBRACE
	{ { fname = $2;
			freturn = $1;
			formals = $4;
			locals = List.rev $7;
			body = List.rev $8 } }

formals_opt:
	/* nothing */ { [] }
	| formal_list { List.rev($1) }

formal_list:
	vdecl { [$1] }
	| formal_list COMMA vdecl { $3 :: $1 }

vdecl_list:
	/* nothing */ { [] }
	| vdecl_list vdecl { $2 :: $1 }

vdecl:
	TYPE ID SEMI { { varname = $2; vartype = $1 } }

stmt_list:
	/* nothing */ { [] }
	| stmt_list stmt { $2 :: $1 }

stmt:
    expr SEMI { Expr($1) }
  | RETURN expr SEMI { Return($2) }
  | LBRACE stmt_list RBRACE { Block(List.rev $2) }
  | IF LPAREN expr RPAREN stmt %prec NOELSE { If($3, $5, Block([])) }
  | IF LPAREN expr RPAREN stmt ELSE stmt  { If($3, $5, $7) }
  | FOR LPAREN expr_opt SEMI expr_opt SEMI expr_opt RPAREN stmt
     { For($3, $5, $7, $9) }
  | WHILE LPAREN expr RPAREN stmt { While($3, $5) }

expr_opt:
    /* nothing */ { Noexpr }
  | expr          { $1 }

expr:
  ID                   { Id($1) }
	| INTLITERAL         { IntLiteral($1) }
	| BOOLEANLITERAL     { BoolLiteral($1) }
	| STRINGLITERAL      { StringLiteral($1) }
	| CARDLITERAL        { CardLiteral($1) }
	| LABELLITERAL       { LabelLiteral($1) }
  | expr PLUS   expr   { Binop($1, Add,       $3) }
  | expr MINUS  expr   { Binop($1, Sub,       $3) }
  | expr TIMES  expr   { Binop($1, Mult,      $3) }
  | expr DIVIDE expr   { Binop($1, Div,       $3) }
	| expr MOD    expr   { Binop($1, Mod,       $3) }
  | expr EQ     expr   { Binop($1, Equal,     $3) }
  | expr NEQ    expr   { Binop($1, Neq,       $3) }
  | expr LT     expr   { Binop($1, Less,      $3) }
  | expr LEQ    expr   { Binop($1, Leq,       $3) }
  | expr GT     expr   { Binop($1, Greater,   $3) }
  | expr GEQ    expr   { Binop($1, Geq,       $3) }
	| expr AND    expr   { Binop($1, And,       $3) }
	| expr OR     expr   { Binop($1, Or,        $3) }
  | expr ASSIGN expr   { Assign($1,           $3) }
	| NOT    expr        { Not($2) }
	| expr PLUSPLUS      { Assign($1, Binop($1, Add, IntLiteral(1))) }
	| expr MINUSMINUS    { Assign($1, Binop($1, Sub, IntLiteral(1))) }
	| expr PLUSEQ expr   { Assign($1, Binop($1, Add, $3)) }
	| expr MINUSEQ expr  { Assign($1, Binop($1, Sub, $3)) }
	| expr TIMESEQ expr  { Assign($1, Binop($1, Mult, $3)) }
	| expr DIVIDEEQ expr { Assign($1, Binop($1, Div, $3)) }
	| expr MODEQ expr    { Assign($1, Binop($1, Mod, $3)) }
  | LPAREN expr RPAREN { $2 }
	| QUOTE expr QUOTE   { $2 }
	| LBRACKET list_opt RBRACKET { ListLiteral($2) }
  | ID LPAREN actuals_opt RPAREN { Call($1, $3) }


list_opt:
	/* nothing */            { [] }
	| list_list              { List.rev $1 }

list_list:
	expr                     { [$1] }
	| list_list COMMA expr   { $3 :: $1 }
	
actuals_opt:
    /* nothing */ { [] }
  | actuals_list  { List.rev $1 }

actuals_list:
    expr                    { [$1] }
  | actuals_list COMMA expr { $3 :: $1 }